﻿using Developer.Core.FrameWork.RedisExtensions.RedisSubscribeInit.Abstractions;
using Developer.Core.FrameWork.RedisExtensions.RedisSubscribeInit.Attributes;
using Developer.Core.FrameWork.RedisExtensions.RedisSubscribeInit.Cache;
using Developer.Core.FrameWork.RedisExtensions.RedisSubscribeInit.Internals;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace Developer.Core.FrameWork.RedisExtensions.RedisSubscribeInit.InitMq
{
    public class InitMqCore
    {
        /// <summary>
        /// 发送事件
        /// </summary>
        /// <param name="ExecutorDescriptorList"></param>
        /// <param name="serviceProvider"></param>
        /// <param name="options"></param>
        private void Send(IEnumerable<ConsumerExecutorDescriptor> ExecutorDescriptorList, IServiceProvider serviceProvider, InitMqOption options)
        {
            foreach (ConsumerExecutorDescriptor executorDescriptor in ExecutorDescriptorList)
            {
                ConsumerExecutorDescriptor ConsumerExecutorDescriptor = executorDescriptor;
                Task.Run((Func<Task>)(() =>
                {
                    using (IServiceScope scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
                    {
                        string name = ConsumerExecutorDescriptor.Attribute.Name;
                        object obj = ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, (Type)ConsumerExecutorDescriptor.ImplTypeInfo);
                        ParameterInfo[] parameterInfos = ConsumerExecutorDescriptor.MethodInfo.GetParameters();
                        ICacheService service = scope.ServiceProvider.GetService<ICacheService>();
                        while (true)
                        {
                            try
                            {
                                if (options.ShowLog)
                                    Console.WriteLine(string.Format("执行方法:{0},key:{1},执行时间{2}", (object)obj.ToString(), (object)name, (object)DateTime.Now));
                                if (service.ListLength(name) > 0L)
                                {
                                    string res = service.ListRightPop(name);
                                    Thread.Sleep(options.IntervalTime);
                                    try
                                    {
                                        Task.Run((Action)(() =>
                                        {
                                            if (parameterInfos.Length == 0)
                                            {
                                                ConsumerExecutorDescriptor.MethodInfo.Invoke(obj, (object[])null);
                                            }
                                            else
                                            {
                                                object[] parameters = new object[1]
                                                {
                                                   (object) res
                                                };
                                                ConsumerExecutorDescriptor.MethodInfo.Invoke(obj, parameters);
                                            }
                                        }));
                                    }
                                    catch (Exception ex)
                                    {
                                        Console.WriteLine(ex.Message);
                                    }
                                }
                                else
                                    Thread.Sleep(options.SuspendTime);
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.Message);
                            }
                        }
                    }
                }));
            }
        }

        /// <summary>
        /// 发现订阅事件
        /// </summary>
        /// <param name="provider">容器提供方</param>
        /// <param name="options">事件总线配置</param>
        public void FindInterfaceTypes(IServiceProvider provider, InitMqOption options)
        {
            // 定义消费者集合
            List<ConsumerExecutorDescriptor> executorDescriptorList = new List<ConsumerExecutorDescriptor>();
            using (IServiceScope scope = provider.CreateScope())
            {
                Func<Type, IRedisSubscribe> service = scope.ServiceProvider.GetService<Func<Type, IRedisSubscribe>>();
                foreach (Type type in (IEnumerable<Type>)options.ListSubscribe)
                {
                    TypeInfo typeInfo = service(type).GetType().GetTypeInfo();
                    if (typeof(IRedisSubscribe).GetTypeInfo().IsAssignableFrom(typeInfo))
                        executorDescriptorList.AddRange(this.GetTopicAttributesDescription(typeInfo));
                }
                this.Send((IEnumerable<ConsumerExecutorDescriptor>)executorDescriptorList, provider, options);
            }
        }

        private IEnumerable<ConsumerExecutorDescriptor> GetTopicAttributesDescription(
          TypeInfo typeInfo)
        {
            foreach (MethodInfo declaredMethod in typeInfo.DeclaredMethods)
            {
                MethodInfo method = declaredMethod;
                IEnumerable<TopicAttribute> customAttributes = method.GetCustomAttributes<TopicAttribute>(true);
                if (!(customAttributes is IList<TopicAttribute> topicAttributeList))
                    topicAttributeList = (IList<TopicAttribute>)customAttributes.ToList<TopicAttribute>();
                IList<TopicAttribute> source = topicAttributeList;
                if (source.Any<TopicAttribute>())
                {
                    foreach (TopicAttribute attr in (IEnumerable<TopicAttribute>)source)
                        yield return this.InitDescriptor(attr, method, typeInfo);
                    method = (MethodInfo)null;
                }
            }
        }

        private ConsumerExecutorDescriptor InitDescriptor(
          TopicAttribute attr,
          MethodInfo methodInfo,
          TypeInfo implType)
        {
            return new ConsumerExecutorDescriptor()
            {
                Attribute = attr,
                MethodInfo = methodInfo,
                ImplTypeInfo = implType
            };
        }
    }
}
